/*
 * // +-------------------------------------------------------------------------------------------------
 * // |                 有你就好 [ 有节骨乃坚，无心品自端 ]     <http://encoding.wang>
 * // +-------------------------------------------------------------------------------------------------
 * // |                             独在异乡为异客         每逢佳节倍思亲
 * // +-------------------------------------------------------------------------------------------------
 * // |                 联系:   <707069100@qq.com>      <http://weibo.com/513778937>
 * // +-------------------------------------------------------------------------------------------------
 */

// -----------------------------------------------------------------------------------------------------
// +----------------------------------------------------------------------------------------------------
// |                   ErYang出品 属于小极品          共同学习    共同进步
// +----------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------


package wang.encoding.mroot.admin.common.shiro


import org.apache.shiro.subject.Subject
import org.apache.shiro.web.filter.AccessControlFilter
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import wang.encoding.mroot.admin.common.constant.ConfigConst
import wang.encoding.mroot.admin.common.util.ShiroSessionUtil
import wang.encoding.mroot.common.exception.BaseException
import wang.encoding.mroot.common.util.StringUtil
import wang.encoding.mroot.model.entity.system.Role
import wang.encoding.mroot.model.entity.system.Rule
import wang.encoding.mroot.model.entity.system.User
import wang.encoding.mroot.model.enums.StatusEnum
import wang.encoding.mroot.service.system.RoleService
import wang.encoding.mroot.service.system.RuleService
import wang.encoding.mroot.service.system.UserService
import java.math.BigInteger
import java.util.*
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse


/**
 * shiro 访问控制过滤器
 */
@Component
class MyShiroFilter : AccessControlFilter() {

    companion object {

        private val logger: Logger = LoggerFactory.getLogger(MyShiroFilter::class.java)

        /**
         * 错误页面地址
         */
        private const val ERROR404_URL: String = "/error/404"

    }

    // -------------------------------------------------------------------------------------------------

    @Autowired
    private lateinit var configProperties: ConfigConst

    @Autowired
    private lateinit var userService: UserService

    @Autowired
    private lateinit var ruleService: RuleService

    @Autowired
    private lateinit var roleService: RoleService

    @Value("\${server.servlet.contextPath}")
    private val contextPath: String? = null


    // -------------------------------------------------------------------------------------------------

    /**
     * 类似于 AOP 中的前置增强；在拦截器链执行之前执行；
     * 如果返回true 则继续拦截器链；否则中断后续的拦截器链的执行直接返回；
     * 进行预处理（如基于表单的身份验证、授权）
     *
     * @param servletRequest  ServletRequest
     * @param servletResponse ServletResponse
     *
     * @return true/false
     * @throws Exception Exception
     */
    @Throws(BaseException::class)
    override fun preHandle(servletRequest: ServletRequest, servletResponse: ServletResponse): Boolean {
        val subject: Subject = getSubject(servletRequest, servletResponse) ?: return false

        val request: HttpServletRequest = servletRequest as HttpServletRequest

        var url: String = request.requestURI
        // 根目录
        if (url == "/") {
            return true
        }
        // 登录页面
        if (url.startsWith(contextPath + configProperties.adminLoginUrlName)) {
            return true
        }
        // 处理登录
        if (url.startsWith(contextPath + configProperties.adminDoLoginUrlName)) {
            return true
        }
        // 退出页面
        if (url.startsWith(contextPath + configProperties.adminLogoutUrlName)) {
            return true
        }
        // 验证码
        if (url.startsWith(contextPath + configProperties.adminVerifyImageUrlName)) {
            return true
        }
        // 资源文件
        if (url.startsWith(contextPath + configProperties.adminStaticPathUrlName)) {
            return true
        }
        // 无权限页面
        if (url.startsWith(contextPath + configProperties.adminUnauthorizedUrlName)) {
            return true
        }
        // 改变语言
        if (url.startsWith(contextPath + configProperties.adminLanguageUrlName)) {
            return true
        }
        // 百度编辑器
//        if (url.startsWith("$contextPath/ueditor")) {
//            return true
//        }

        // 错误页面
        if (url.startsWith(contextPath + ERROR404_URL)) {
            return true
        }

        // 得到登录的用户
        val onlineUser: User? = ShiroSessionUtil.getAttribute(configProperties.adminSessionName) as User?
        // 得到用户的权限菜单
        val menu: List<*>? = ShiroSessionUtil.getAttribute(configProperties.adminMenuName) as List<*>?
        if (null == onlineUser || null == menu) {
            val username: String = subject.principal as String? ?: return true
            val user: User? = userService.getByUsername(username)
            if (null == user || StatusEnum.DISABLE.key == user.status) {
                // 跳转到后台登录页面
                saveRequestAndRedirectToLogin(servletRequest, servletResponse)
                return false
            }
            // 权限
            val rules: MutableList<Rule> = mutableListOf()
            // 所属角色
            val roles: Set<Role> = roleService.listByUserId(user.id!!)!!
            if (roles.isNotEmpty()) {
                for (role: Role in roles) {
                    val ruleList: TreeSet<Rule>? = ruleService.listByRoleId(role.id!!)
                    if (null !== ruleList && ruleList.isNotEmpty()) {
                        for (rule: Rule in ruleList) {
                            if (BigInteger.ZERO != rule.pid) {
                                rules.add(rule)
                            }
                        }
                    }
                }
                if (rules.isNotEmpty()) {
                    // list 转为 tree
                    val tree: List<Rule>? = Rule.list2Tree(rules)
                    if (null != tree) {
                        // 用户权限菜单存放在 session 中
                        ShiroSessionUtil.setAttribute(configProperties.adminMenuName, tree)
                    }
                } else {
                    saveRequestAndRedirectToLogin(servletRequest, servletResponse)
                    return false
                }
            } else {
                saveRequestAndRedirectToLogin(servletRequest, servletResponse)
                return false
            }
        }

        // 移除前缀
        if (url.startsWith(contextPath!!)) {
            url = url.removePrefix(contextPath)
        }


        val count: Int = StringUtil.countCharInnerContent(url, "/")
        if (3 <= count) {
            // 移除带/的地址 /admin/user/edit/* 变成 /admin/user/edit
            url = url.substring(0, url.lastIndexOf("/"))
        }

        // 权限校验 判断是否包含权限
        // 具体响应 ShiroDbRealm 中 doGetAuthorizationInfo 方法，判断是否包含此 url 的响应权限
        val isPermitted: Boolean = subject.isPermitted(url)
        return if (isPermitted) {
            true
        } else {
            // 跳转到无权限异常页面
            //ShiroSessionUtil.removeAttribute(configProperties.adminSessionName)
            //ShiroSessionUtil.removeAttribute(configProperties.adminMenuName)
            //subject.logout()
            val response: HttpServletResponse = servletResponse as HttpServletResponse
            response.sendRedirect(contextPath + configProperties.adminUnauthorizedUrlName)
            false
        }
    }

    // -------------------------------------------------------------------------------------------------

    /**
     * 表示是否允许访问；o 就是 [urls] 配置中拦截器参数部分，
     * 如果允许访问返回true，否则false；
     *
     * @param servletRequest  ServletRequest
     * @param servletResponse ServletResponse
     * @param o               Object
     * @return true/false
     * @throws Exception
     */
    @Throws(BaseException::class)
    override fun isAccessAllowed(servletRequest: ServletRequest, servletResponse: ServletResponse,
                                 o: Any): Boolean {
        val subject: Subject = getSubject(servletRequest, servletResponse)
        return null != subject.principal
    }

    // -------------------------------------------------------------------------------------------------

    /**
     * onAccessDenied：表示当访问拒绝时是否已经处理了；如果返回 true 表示需要继续处理；
     * 如果返回 false 表示该拦截器实例已经处理了，将直接返回即可
     *
     * @param servletRequest  servletRequest
     * @param servletResponse servletResponse
     * @return true/false
     */
    override fun onAccessDenied(servletRequest: ServletRequest, servletResponse: ServletResponse): Boolean {
        return true
    }

    // -------------------------------------------------------------------------------------------------

}

// -----------------------------------------------------------------------------------------------------

// End MyShiroFilter class

/* End of file MyShiroFilter.kt */
/* Location: ./src/main/kotlin/wang/encoding/mroot/admin/common/shiro/MyShiroFilter.kt */

// -----------------------------------------------------------------------------------------------------
// +----------------------------------------------------------------------------------------------------
// |                           ErYang出品 属于小极品  O(∩_∩)O~~   共同学习    共同进步
// +----------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------
